import { EventEmitter } from 'events';
import { WebSocketClient } from './websocket/client';
import { RemoteConfig } from './remote';

export enum EndpointStatus {
  Disconnected = 'Disconnected',
  Connecting = 'Connecting',
  Connected = 'Connected',
  Failed = 'Failed'
}

export class Endpoint extends EventEmitter {
  private readonly address: string;
  private readonly config: RemoteConfig;
  private client?: WebSocketClient;
  private _status: EndpointStatus = EndpointStatus.Disconnected;

  constructor(address: string, config: RemoteConfig) {
    super();
    this.address = address;
    this.config = config;
    console.log(`[ENDPOINT] Created endpoint for address: ${address}`);
  }

  get status(): EndpointStatus {
    return this._status;
  }

  private setStatus(status: EndpointStatus): void {
    this._status = status;
    console.log(`[ENDPOINT] Status changed to ${status} for ${this.address}`);
  }

  async connect(): Promise<void> {
    console.log(`[ENDPOINT] Connecting to ${this.address}...`);
    
    if (this.status === EndpointStatus.Connected) {
      console.log(`[ENDPOINT] Already connected to ${this.address}`);
      return;
    }

    this.setStatus(EndpointStatus.Connecting);

    try {
      const [hostname, portStr] = this.address.split(':');
      const port = parseInt(portStr, 10);
      
      if (!hostname || isNaN(port)) {
        throw new Error(`Invalid address format: ${this.address}, expected host:port`);
      }
      
      console.log(`[ENDPOINT] Creating WebSocket client for ${hostname}:${port}`);
      this.client = new WebSocketClient(this.config);
      
      this.client.on('connected', () => {
        console.log(`[ENDPOINT] WebSocket connected to ${this.address}`);
        this.setStatus(EndpointStatus.Connected);
        this.emit('connected');
      });

      this.client.on('disconnected', () => {
        console.log(`[ENDPOINT] WebSocket disconnected from ${this.address}`);
        this.setStatus(EndpointStatus.Disconnected);
        this.emit('disconnected');
      });

      this.client.on('error', (error: Error) => {
        console.error(`[ENDPOINT] WebSocket error for ${this.address}:`, error);
        this.setStatus(EndpointStatus.Failed);
        this.emit('error', error);
      });

      this.client.on('message', (message: unknown) => {
        console.log(`[ENDPOINT] Received message from ${this.address}`);
        this.emit('message', message);
      });

      console.log(`[ENDPOINT] Connecting WebSocket to ${this.address}...`);
      await this.client.connect(hostname, port);
      console.log(`[ENDPOINT] Connection established with ${this.address}`);
    } catch (error: any) {
      console.error(`[ENDPOINT] Failed to connect to ${this.address}:`, error.message || error);
      this.setStatus(EndpointStatus.Failed);
      throw error;
    }
  }

  async send(message: unknown): Promise<void> {
    console.log(`[ENDPOINT] Sending message to ${this.address}, current status: ${this.status}`);
    
    if (!this.client || this.status !== EndpointStatus.Connected) {
      console.log(`[ENDPOINT] Not connected, attempting to connect to ${this.address}...`);
      try {
        await this.connect();
        console.log(`[ENDPOINT] Successfully connected to ${this.address}`);
      } catch (error: any) {
        console.error(`[ENDPOINT] Connection failed: ${error.message}`);
        throw error;
      }
    }
    
    try {
      console.log(`[ENDPOINT] Preparing message for ${this.address}`);
      
      // 深度清理消息，确保可序列化
      const cleanedMessage = this.deepCleanMessage(message);
      
      // 验证序列化
      let serializedMsg: string;
      try {
        serializedMsg = JSON.stringify(cleanedMessage);
        console.log(`[ENDPOINT] Message successfully serialized, length: ${serializedMsg.length}`);
      } catch (error: any) {
        console.error(`[ENDPOINT] Message serialization failed even after cleaning: ${error.message}`);
        console.error(`[ENDPOINT] Failed message keys:`, Object.keys(cleanedMessage as object));
        
        // 作为最后手段，尝试更激进的清理
        const emergencyMessage = {
          type: (cleanedMessage as any)?.type || 'Unknown',
          targetPID: {
            address: (cleanedMessage as any)?.targetPID?.address || 'unknown',
            id: (cleanedMessage as any)?.targetPID?.id || 'unknown'
          },
          messageId: (cleanedMessage as any)?.messageId || `emergency-${Date.now()}`,
          message: {
            type: 'emergency-fallback',
            originalType: typeof (cleanedMessage as any)?.message === 'object' 
                        ? (cleanedMessage as any)?.message?.type || 'unknown'
                        : typeof (cleanedMessage as any)?.message
          }
        };
        
        serializedMsg = JSON.stringify(emergencyMessage);
        console.log(`[ENDPOINT] Using emergency serialized message: ${serializedMsg}`);
      }
      
      // 设置重试逻辑
      let retries = 3;
      const maxDelay = 2000; // 最大重试延迟（毫秒）
      
      const sendWithRetry = async (attempt: number): Promise<void> => {
        try {
          if (!this.client) {
            throw new Error('WebSocket client is not initialized');
          }
          
          await this.client.send(serializedMsg);
          console.log(`[ENDPOINT] Message sent to ${this.address} successfully`);
        } catch (error: any) {
          console.error(`[ENDPOINT] Send attempt ${attempt} failed:`, error.message);
          
          if (attempt < retries) {
            // 计算指数退避延迟
            const delay = Math.min(Math.pow(2, attempt) * 100, maxDelay);
            console.log(`[ENDPOINT] Retrying in ${delay}ms...`);
            
            await new Promise(resolve => setTimeout(resolve, delay));
            
            // 如果连接断开，尝试重新连接
            if (this.status !== EndpointStatus.Connected) {
              console.log(`[ENDPOINT] Reconnecting before retry...`);
              await this.connect();
            }
            
            return sendWithRetry(attempt + 1);
          }
          
          console.error(`[ENDPOINT] All retry attempts failed`);
          this.setStatus(EndpointStatus.Failed);
          throw error;
        }
      };
      
      await sendWithRetry(1);
      
    } catch (error: any) {
      console.error(`[ENDPOINT] Failed to send message to ${this.address}:`, error);
      throw error;
    }
  }

  // 深度清理消息对象，移除循环引用和不可序列化内容
  private deepCleanMessage(message: unknown, seen = new WeakSet(), path = ''): unknown {
    // 处理原始类型或 null
    if (message === null || message === undefined || typeof message !== 'object') {
      return message;
    }
    
    // 处理循环引用
    if (seen.has(message)) {
      console.log(`[ENDPOINT] Detected circular reference at path: ${path}`);
      return "[Circular Reference]";
    }
    
    // 将对象加入已处理的集合
    seen.add(message);
    
    // 处理数组
    if (Array.isArray(message)) {
      console.log(`[ENDPOINT] Processing array at path: ${path}, length: ${message.length}`);
      return message.map((item, index) => this.deepCleanMessage(item, seen, `${path}[${index}]`));
    }
    
    // 处理日期
    if (message instanceof Date) {
      return message.toISOString();
    }
    
    // 处理 Error
    if (message instanceof Error) {
      console.log(`[ENDPOINT] Converting Error to object at path: ${path}`);
      return { 
        error: message.message, 
        stack: message.stack,
        name: message.name
      };
    }
    
    // 检查是否是 PID 或类似 PID 的对象
    const isPID = message.constructor && message.constructor.name === 'PID';
    const isPIDLike = 'address' in message && 'id' in message && 
                   typeof (message as any).address === 'string' && 
                   typeof (message as any).id === 'string';
    
    if (isPID || isPIDLike) {
      console.log(`[ENDPOINT] Processing ${isPID ? 'PID' : 'PID-like'} object at path: ${path}`);
      console.log(`[ENDPOINT] PID details: ${(message as any).address}/${(message as any).id}`);
      
      // 确保我们只保留必要的 PID 属性
      return {
        address: (message as any).address,
        id: (message as any).id
      };
    }
    
    // 处理一般对象
    console.log(`[ENDPOINT] Processing object at path: ${path}, type: ${message.constructor?.name || 'Object'}`);
    const result: any = {};
    
    for (const [key, value] of Object.entries(message)) {
      // 跳过函数和私有属性
      if (typeof value === 'function' || key.startsWith('_')) {
        console.log(`[ENDPOINT] Skipping ${typeof value === 'function' ? 'function' : 'private property'} at ${path}.${key}`);
        continue;
      }
      
      // 处理特殊情况
      if (key === 'system' || key === 'actorSystem' || key === 'client' || key === 'ws') {
        console.log(`[ENDPOINT] Skipping system property at ${path}.${key}`);
        continue;
      }
      
      // 递归处理其他属性
      try {
        result[key] = this.deepCleanMessage(value, seen, `${path}.${key}`);
      } catch (error: any) {
        console.error(`[ENDPOINT] Error cleaning property ${path}.${key}:`, error);
        result[key] = `[Error: ${error.message || 'Unknown error'}]`;
      }
    }
    
    return result;
  }

  /**
   * 断开连接
   */
  async disconnect(): Promise<void> {
    console.log(`[ENDPOINT] Disconnecting from ${this.address}`);
    if (this.client) {
      try {
        this.client.close();
        this.client = undefined;
        this.setStatus(EndpointStatus.Disconnected);
        console.log(`[ENDPOINT] Disconnected from ${this.address}`);
      } catch (error) {
        console.error(`[ENDPOINT] Error disconnecting from ${this.address}:`, error);
        throw error;
      }
    }
  }
  
  /**
   * 关闭连接（同步版本）
   */
  close(): void {
    console.log(`[ENDPOINT] Closing connection to ${this.address}`);
    if (this.client) {
      try {
        this.client.close();
        this.client = undefined;
        this.setStatus(EndpointStatus.Disconnected);
        console.log(`[ENDPOINT] Closed connection to ${this.address}`);
      } catch (error) {
        console.error(`[ENDPOINT] Error closing connection to ${this.address}:`, error);
      }
    }
  }
} 